// 内部是通过readFile来读取文件的

// __filename 是文件所在的完整路径
// __dirname  文件所在的文件夹”绝对路径“
// 路径是死的无法更改
// console.log(exports,require,module,__filename,__dirname);
// (function(exports,require,module,__filename,__dirname){
//     // code....
// }(参数))

// 都是node中函数提供的， node中没有浏览器 没有window

// require的实现
// 1.Module._resolveFilename 解析文件里的文件名 （将路径添加了后缀并且解析出他的绝对路径）
// 2.检查是否缓存过？ 没有就需要加载模块
// 3. new Module(filename, parent); 创建模块module  {id：文件路径,exports:{}}  exports 是模块最终导出的结果
// 4.将模块缓存起来
// 5.加载这个模块，根据文件的后缀名来决定如何加载.内部采用了一个策略模式来进行模块的加载

// 中间的加载 过程就是想办法给exports 赋值
// 核心加载过程就是读取这个文件 fs.readFileSync
// 将内容 进行包裹  (function(module){ module.exports = 'a'})()
// 最终返回的是module.exports; -> a

// > 读文件 + 包函数，将modulue对象传入，传入后用户在自己的逻辑里会给module.exports赋值

const path = require("path");
const fs = require("fs");
const vm = require("vm"); // 创建一个沙箱环境
function Module(id) {
  this.id = id;
  this.exports = {}; // 返回结果
}
Module._extentions = {
  ".js"(module) {
    const content = fs.readFileSync(module.id, "utf8");

    // new Function注意事项  eval缺陷
    const fn = vm.compileFunction(content, [
      "exports",
      "require",
      "module",
      "__filename",
      "__dirname",
    ]);

    const thisValue = module.exports; // exports 就是用户需要的结果
    const require = myRequire; // 加载方法
    const __filename = module.id; // 文件名
    const __dirname = path.dirname(module.id); // 目录名
    Reflect.apply(fn, thisValue, [ // Reflect  Relect.get()  Reflect.defineProperty Reflect.ownKeys
      thisValue,
      require,
      module,
      __filename,
      __dirname,
    ]);
  },
  ".json"(module) {
    // 自己的加载器手动的返回的
    const jsonStr = fs.readFileSync(module.id, "utf8");
    module.exports = JSON.parse(jsonStr); // 直接将json赋予给 exports 就可以了
  },
  ".md"(module) {
    const mdStr = fs.readFileSync(module.id, "utf8");
    module.exports = mdStr;
  },
};
// 找文件
Module._resolveFilename = function (id) {
  const filepath = path.resolve(__dirname, id);
  if (fs.existsSync(filepath)) {
    // 此方法是判断文件是否存在，如果存在则直接返回
    return filepath;
  }
  const exts = Object.keys(Module._extentions);
  for (let i = 0; i < exts.length; i++) {
    const filepath = path.resolve(__dirname, id) + exts[i];
    if (fs.existsSync(filepath)) {
      return filepath;
    }
  }
  throw new Error("Cannot find module" + `${id}`);
};
Module._cache  = {}
Module.prototype.load = function () {
  const extension = path.extname(this.id);
  Module._extentions[extension](this); // 告诉对应的加载器按照需要去加载吧~
};
function myRequire(id) {
  let absPath = Module._resolveFilename(id);
  const existsModule = Module._cache[absPath];
  if(existsModule){
    return existsModule.exports; // 将上次缓存的结果返回
  }
  const module = new Module(absPath);
  module.load();
  Module._cache[absPath] = module; // 将结果缓存起来
  return module.exports; // 最终的返回结果 -> a
}

let a = require("./a.js"); // a.js a.json a.node  同步的过程
console.log(a); // 'a'

// require方法是运行时来解析依赖 （动态加载）
// 循环依赖（commonjs规范中没有错误） esModule会出错 (避免循环依赖，手动切断依赖连接) 需要我们合理划分模块，采用非强制依赖的关系

// esModule 和 commonjs的区别？

// 如果在commonjs中同时给 exports和module.exports赋值，只有module.exports会生效
// es6中

